// inspired from http://www.labri.fr/perso/soueidan/pmwiki.php?n=References.OcamlParserTutorialSetup
#include "parserHelper.h"
using namespace std;


bool ParserHelper::isDigit(char c)
{
	return ( c >= '0' and c <=  '9');
}

bool ParserHelper::isSign(char c)
{
	return ( c == '-' or c == '+');
}

bool ParserHelper::isPunctuation(char c)
{
	return ( c == '.' or c == ',' or c == ':' or c == ';');
}

bool ParserHelper::isSeparator(char c)
{
	return ( c == '/' );
}
bool ParserHelper::isAlpha(char c)
{
	return ( c >= 'A' and c <=  'z' or c == '$' or c == '(' or c == ')');
}

bool ParserHelper::isComment(char c)
{
	return  c == '#' ;
}

Lexeme ParserHelper::lexer(ifstream& source)
{
	Lexeme lex;
	char c ;
	while(source.good())
	{
		c = source.get();
		switch(c)
		{
			case ' ':
			case '\t':
				return lexer(source);
			case '\n':
				lex.type = EOL;
				return lex;
			case -1:
				lex.type = EOFF;
				return lex;
			default:
				if(ParserHelper::isDigit(c) or ParserHelper::isSign(c))
				{
					int num = 0;
					double sign = 1.0;
					int exposant = 0;
					if(c == '-')
						sign = -1.0;
					if(ParserHelper::isSign(c))
						c = source.get();
					do { num = 10 * num + c - '0'; } while( (c = source.get()) != EOF and ParserHelper::isDigit(c));
					if(c == '.')
					{
						double real = num;
						double mul = 0.1;
						c = source.get();
						if(!isDigit(c))
							throw ParserException("Failed to parse digit");
						else
							do {real = real + (c - '0')*mul; mul *= 0.1;} while( (c = source.get()) != EOF and ParserHelper::isDigit(c));

						if(c == 'e'or c == 'E')
						{
							float expSign = 1.0;
							c = source.get();
							if(c == '-')
							{
								expSign = -1.0;
								c = source.get();
							}
							else if(c == '+')
							{
								expSign = 1.0;
								c = source.get();
							}
							do {exposant = 10 * exposant + (c - '0');} while( (c = source.get()) != EOF and ParserHelper::isDigit(c));
							real = real * pow(10.0, (double)(expSign * exposant));
						}
						source.putback(c);
						lex.real = sign * real;
						lex.type = REAL;
					}
					else
					{
						source.putback(c);
						lex.integer = (int) sign * num;
						lex.type = INTEGER;
					}
					return lex;
				}
				if(ParserHelper::isAlpha(c))
				{
					string alphaS;
					string tmp = "a";
					tmp[0] = c;
					do { alphaS += tmp; }while( (tmp[0] = source.get()) != EOF and ParserHelper::isAlpha(tmp[0]));
					source.putback(tmp[0]);
					char* alpha = new char[alphaS.size()+1];
					strcpy(alpha, alphaS.c_str());
					lex.alpha = alpha;
					lex.type = ALPHA;
					return lex;
				}
				if(ParserHelper::isPunctuation(c))
				{
					lex.punctuation = c;
					lex.type = PUNC;
					return lex;
				}
				if(ParserHelper::isSeparator(c))
				{
					lex.punctuation = c;
					lex.type = SEPARATOR;
					return lex;
				}
				if(ParserHelper::isComment(c))
				{
					string comment;
					char* alpha = new char[256+1];
					source.getline(alpha, 256);
					lex.alpha = alpha;
					lex.type = COMMENT;
					return lex;
				}
			std::cout << "unknown char :-> " << (int)c << std::endl;
			throw ParserException("Failed to parse entry");
		}
	}
	return lex;
}

std::ostream& operator << (std::ostream& os, const Lexeme& lex)
{
	if(lex.type == ALPHA)
		return os << lex.alpha;
	if(lex.type == PUNC)
		return os << lex.punctuation;
	if(lex.type == INTEGER)
		return os << lex.integer;
	if(lex.type == REAL)
		return os << lex.real;
}
